
makeGuardSpr = () ->
  standingUp = new window.Animation "standingUp", [[3, 4]]
  standingDown = new window.Animation "standingDown", [[0, 4]]
  standingLeft = new window.Animation "standingLeft", [[5, 5]]
  standingRight = new window.Animation "standingRight", [[0, 5]]

  runningUp = new window.Animation "runningUp", [[3, 4], [4, 4], [3, 4], [5, 4]]
  runningUp.fps 10
  runningDown = new window.Animation "runningDown", [[0, 4], [1, 4], [0, 4], [2, 4]]
  runningDown.fps 10
  runningRight = new window.Animation "runningRight", [[0, 5], [1, 5], [0, 5], [2, 5]]
  runningRight.fps 10
  runningLeft = new window.Animation "runningLeft", [[5, 5], [4, 5], [5, 5], [3, 5]]
  runningLeft.fps 10

  spr = new window.Sprite 16, 16
  spr.addAnimation new window.Animation "dead", [[6, 4]]
  spr.addAnimation standingUp
  spr.addAnimation standingDown
  spr.addAnimation standingLeft
  spr.addAnimation standingRight
  spr.addAnimation runningUp
  spr.addAnimation runningDown
  spr.addAnimation runningRight
  spr.addAnimation runningLeft
  spr.current = spr.standingDown

  return spr

class GuardStateMachine
  #Initialization code for guard
  init : (@g) ->
    @game = @g.g
    @nextCheck = Date.now() + 2000 + 1000 * Math.random()

    return @
  #Stepping code, what bussiness does he do
  stateInit : ->
  step : ->
  slideCollide : ->
  collidedWith : ->

  draw : ->

  hear : (x, y) ->
    if @g.state.isAlert == true
      return

    s = new GuardAlert()
    @g.alertedGuardsCount += 1
    s.ax = x
    s.ay = y
    console.log "Guard is alert!"
    @g.changeState s

  checkForDistractingObjects : ->
    return false if Date.now() < @nextCheck
    @nextCheck = Date.now() + 2000 + 1000 * Math.random()

    corpses = @game.getEntitiesWithTag "corpse"

    for c in corpses
      if @g.hasSeen(c) == true 
        continue 
      
      if @g.canSee c
        @g.seen c
        
        alert = new GuardAlert(Date.now() + 5000 + 5000 * Math.random())
        
        @g.alertedGuardsCount += 1
        @g.changeState alert
        alert.x = c.x
        alert.y = c.y
        return true

    return false

  checkPlayer : ->
    return false if @game == undefined
    p = @game.getEntitiesWithTag("player")[0]
    
    if p == undefined
      console.log "Player is does not exist."
      return false

    if p.isDead()
      return false 

    if @g == undefined
      return false

    if not @g.canSee p
      return false
    
    a = new GuardAggressive()
    @g.changeState a
    a.lastKnownX = p.x
    a.lastKnownY = p.y
    @game.unnoticed = false
    return true

class GuardAlert extends GuardStateMachine
  init : (@g) -> super @g

  stateInit : (alertDone)->
    alertDone ?= Date.now() + 1000 + 1000 * Math.random()
    @doneBeingAlert = alertDone
    @path = null
    @isAlert = true

  step : ->
    return if @checkPlayer()
    if Date.now() > @doneBeingAlert
      @g.changeState new GuardSearch()
      console.log "No longer alert"
      return
    
    if @path == null
      @path = @game.getPath @g, @ax, @ay
      if @path == null
        @g.changeState new GuardSearch()
        console.log "No longer alert"
        return
      @pos = @path.length - 1

    [px, py] = @path[@pos]
    dx = px - @g.x
    dy = py - @g.y
    l = dx * dx + dy * dy

    if l < 10.0
      @pos -= 1

    @g.move dx, dy, true

    if @pos < 0
      @path = null

    if @path == null
      @g.changeState new GuardSearch()
      console.log "No longer alert"
      return

  draw : ->
    @g.transform.setTranslation @g.x, @g.y - 64
    @game.draw.drawSprite [96, 80], @g.transform



  
class GuardDoNothing extends GuardStateMachine
  init : (@g) -> super @g

  stateInit : ->
    @doneWaiting = Date.now() + 1000 + 1000 * Math.random()

  step : ->
    return if @checkForDistractingObjects()
    return if @checkPlayer()
    if Date.now() > @doneWaiting
      cls = window.rnd.choose [GuardWalkRandom, GuardPatrol]
      @g.changeState new cls()
      return
class GuardWalkRandom extends GuardStateMachine
  init : (@g) -> super @g

  stateInit : ->
    @doneWalking = Date.now() + 1000 + 1000 * Math.random()
    @dx = Math.random() * 2.0 - 1.0
    @dy = Math.random() * 2.0 - 1.0

  step : ->
    return if @checkPlayer()
    return if @checkForDistractingObjects()
    if Date.now() > @doneWalking
      cls = window.rnd.choose [GuardDoNothing, GuardPatrol]
      @g.changeState new cls()
      return
    @g.move @dx, @dy

  slideCollide : ->
    @g.changeState new GuardDoNothing()

class GuardAggressive extends GuardStateMachine
  init : (@g) -> 
    super @g
    return @

  hear : ->

  stateInit : ->
    p = @game?.getEntitiesWithTag("player")[0]

    if p == undefined
      @g.changeState new GuardDoNothing()
      return
    @path = null
    @lastKnownX = p.x
    @lastKnownY = p.y

  canSeePlayer : ->
    p = @game?.getEntitiesWithTag("player")[0]

    if p == undefined
      @g.changeState new GuardDoNothing()
      return true
    @p = p
    if @p.isDead()
      @g.changeState new GuardDoNothing()
      return true

    dx = @p.x - @g.x
    dy = @p.y - @g.y
    if not @game.canSee @g, @p  
      return false

    @path = null
    @lastKnownX = @p.x
    @lastKnownY = @p.y
    @g.move dx, dy, true
    return true

  gotoLastKnownLocation : ->
    if @lastKnownX == undefined or @lastKnownY == undefined
      @g.changeState new GuardSearch()
      return
    if @path == null || @path == undefined
      @path = @game.getPath @g, @lastKnownX, @lastKnownY
      if @path == null
        @g.changeState new GuardSearch()
        return

      if @path.length == 0
        @g.changeState new GuardSearch()
        return
      
      @pos = @path.length - 1

    [px, py] = @path[@pos]
    dx = px - @g.x
    dy = py - @g.y
    l = dx * dx + dy * dy

    if l < 10.0
      @pos -= 1

    @g.move dx, dy, true

    if @pos < 0
      @path = null
    
    if not @game.canSee(@g, @p) and @path == null
      @g.changeState new GuardSearch()
      return


  step : ->
    return if @canSeePlayer()
    @gotoLastKnownLocation()
    

class GuardSearch extends GuardStateMachine
  init : (@g) -> super @g
  
  stateInit : ->
    @path = null
    @pos = 0

    @dests = []
    @dpos = 0
    for x in [5..5 + 5 * Math.random | 0]
      @dests.push [@g.x + 150 * Math.random(), @g.y + 150 * Math.random()]

  goPath : ->
    return false if @path == null
    if @pos < 0
      @path = null
      return false

    [px, py] = @path[@pos]
    dx = px - @g.x
    dy = py - @g.y
    l = dx * dx + dy * dy
    if l < 5.0
      @pos -= 1
    @g.move dx, dy
    return true


  createPath : ->
    if @dpos == @dests.length
      @g.changeState new GuardDoNothing()
      console.log "Search failed could not find anything"
      return true
    [x, y] = @dests[@dpos]
    @path = @game.getPath @g, x, y
    @dpos += 1

  step : ->
    return if @checkPlayer()
    return if @checkForDistractingObjects()
    return if @goPath()
    return if @createPath()


  draw : ->
    @g.transform.setTranslation @g.x, @g.y - 64 + 4.0 * Math.sin(Date.now() / 1000.0)
    @game.draw.drawSprite [112, 80], @g.transform

class GuardDeadState extends GuardStateMachine
  init : (@g) -> super @g
  
class GuardPatrol extends GuardStateMachine
  init : (@g) -> super @g
  
  stateInit : ->
    @path = null
    @pos = 0
    @destx = @x + Math.random() * 400 - 400
    @desty = @y + Math.random() * 400 - 400


  createPath : ->
    return true if @path != null
    return false if @game == undefined
    @path = @game.getPath @g, @destx, @desy
    return false if @path == null
    
    @pos = @path.length - 1
    return true

  step : ->
    return if @checkPlayer()
    return if @checkForDistractingObjects()
    if not @createPath()
      @g.changeState new GuardWalkRandom()
      return

    [px, py] = @path[@pos]
    dx = px - @g.x
    dy = py - @g.y
    l = dx * dx + dy * dy
    if l < 5.0
      @pos -= 1
    @g.move dx, dy
    if @pos < 0
      @g.changeState new GuardDoNothing()
      return

class window.Guard extends window.Entity

  hasSeen : (i) ->
    return @seenItems[i] == true
  
  seen : (i) ->
    @seenItems[i] = true

  constructor : ->
    @seenItems = {}
    @s = @spr = makeGuardSpr()
    @standing = @spr.standingDown
    @mdx = 0
    @mdy = 0
    @attackCD = 0
    @walkSpeed = 1.0
    @runningCooldown = 0
    @lx = 0
    @ly = 0
    @edx = 0
    @edy = 0
    @runningSpeed = 4.0 + Math.random()

    @changeState new GuardDoNothing() if @state == undefined    

  init : (@g) ->
    super(@g)
    @changeState new GuardDoNothing() if @state == undefined
  
  move : (dx, dy, aggresive) ->
    aggresive ?= false
    l = dx * dx + dy * dy
    return if l == 0
    l = Math.sqrt(l)
    dx /= l
    dy /= l
    dx *= @walkSpeed
    dy *= @walkSpeed

    if aggresive and @runningCooldown <= 0
      dx *= @runningSpeed
      dy *= @runningSpeed
      @stammina -= @runningCost
      if @stammina <= 0
        @runningCooldown = 100
    else
      @rest()

    @mdx = dx
    @mdy = dy

  changeState : (s) ->
    @state = s.init @
    @state.stateInit()
    return  @state

  init : (@g) ->
    super @g
    @addTag "guard"

  die : () ->
    @g.killCount += 1
    @s.current = @s.dead
    @changeState(new GuardDeadState())
    @g.deleteEntity @
    e = @g.addEntity new window.Corpse()
    e.x = @x
    e.y = @y
    return if Math.random() > 0.5

    e = @g.addEntity new window.CellKey()

    e.z = 32.0
    e.x = @x
    e.y = @y
    e.dx = Math.random() * 2.0 - 1.0
    e.dy = Math.random() * 2.0 - 1.0
  
  hear : (x, y) ->
    @state.hear x, y

  update : () ->
    @edx *= 0.9
    @edy *= 0.9
    @runningCooldown -= 1
    @mdx = 0
    @attackCD -= 1
    @mdy = 0

    #Movement AI code
    @state.step()

    maxx = Math.max(Math.abs(@mdx), Math.abs(@mdy))

    if maxx == Math.abs @mdx
      if @mdx < 0.0
        @standing = @s.standingLeft
        @s.current = @s.runningLeft
        @lx = -1.0
        @ly = 0.0
      
      if @mdx > 0.0
        @standing = @s.standingRight
        @s.current = @s.runningRight
        @lx = 1.0
        @ly = 0.0
    else
      if @mdy < 0.0
        @standing = @s.standingUp
        @s.current = @s.runningUp
        @lx = 0.0
        @ly = -1.0
      
      if @mdy > 0.0
        @standing = @s.standingDown
        @s.current = @s.runningDown
        @lx = 0.0
        @ly = 1.0
      
    


    if @mdx == 0 and @mdy == 0 and not @isDead()
      @s.current = @standing
    
    @x += @mdx
    @y += @mdy
    @x += @edx
    @y += @edy

  agro : () ->
    if @state.isAggresive != true
      @changeState new GuardAggressive()

  slideCollide : (s) ->
    @x = s[0]
    @y = s[1]
    @state?.slideCollide(s)

    @state.path = null
  
  collidedWith : (o) ->
    return if not o.hasTag "player"
    return if @attackCD > 0
    return if @isDead()

    @state?.collidedWith(o)
    o.hurt 25
    
    o.edx += @lx * 10
    o.edy += @ly * 10

    @attackCD = 15

  render : () ->
    frame = @s.getFrame()
    @transform.setTranslation @x, @y

    @g.draw.drawSprite frame, @transform

    @state.draw()

  canSee : (who) ->
    if who == undefined 
      return false
    dx = who.x - @x
    dy = who.y - @y

    dot = dx * @lx + dy * @ly

    return false if dot < 0.0

    return @g.canSee @, who



  boffx : ->  3 * 4
  boffy : -> 10 * 4
  bwidth : -> 10 * 4
  bheight : -> 6 * 4